﻿package com.zouloux.ui 
{
	// Importation des classes
	import com.zouloux.ui.ScrollView;
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.events.TimerEvent;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextLineMetrics;
	import flash.utils.Timer;
	
	// La classe ScrollController
	public class ScrollController
	{
		// Les dimentions
		protected var _width							:Number;
		protected var _height							:Number;
		
		// La cible et le masque
		protected var _target							:DisplayObject;
		protected var _mask								:Sprite;
		protected var _back								:Sprite;
		
		// Le timer
		protected var _timer							:Timer;
		
		// La position de destination
		protected var _destination						:Number							= 0;
		
		// Le scrollView associé
		protected var _scrollView						:ScrollView;
		
		
		// Les getters
		public function get scrollView ():ScrollView
		{
			return _scrollView;
		}
		
		// Les setters
		public function set scrollView (value:ScrollView):void
		{
			_scrollView = value;
			
			_scrollView.scrollController = this;
			
			// Vérifier si ce scroll n'a pas de parent
			if (_scrollView.parent == null)
			{
				// L'ajouter au parent de la cible
				_target.parent.addChild(_scrollView);
				
				// Et le placer à sa droite
				_scrollView.x = _target.x + _width;
				_scrollView.y = _target.y + (_target is TextField ? 2 : 0);
				_scrollView.totalHeight = _height;
			}
		}
		public function set position (value:Number):void
		{
			// Mesurer la hauteur efféctive
			var totalHeight:Number = _mask.y - _target.height + _mask.height + 2;
			
			// Mesurer la topLimit
			var topLimit = _mask.y - 2;
			
			_target.y = _destination = (value * (totalHeight - topLimit)) + topLimit;
			
			//render();
		}
		
		
		// Le constructeur
		public function ScrollController (target:DisplayObject, width:Number = 0, height:Number = 0)
		{
			//Debug.add("com.zouloux.ui.Scroll{}");
			
			// Créer le timer
			_timer = new Timer(20);
			_timer.addEventListener(TimerEvent.TIMER, render);
			
			// Se souvenir de la cible
			_target = target;
			
			// Si la largeur n'est pas définie, mesurer le champs texte
			if (width == 0)
				_width = target.width;
			
			// Si la hauteur n'est pas définie, mesurer le champs texte
			if (height == 0)
				_height = target.height;
			
			// Vérifier si c'est un textField
			if (target is TextField)
			{
				// Le définir en autoSize
				(target as TextField).autoSize = TextFieldAutoSize.LEFT;
				
				// Ecouter les changements de ce textField
				(target as TextField).addEventListener(Event.CHANGE, startTimer);
			}
			
			// Ecouter l'ajout de la cible à son parent pour pouvoir dessiner le mask
			target.addEventListener(Event.ADDED_TO_STAGE, drawMask);
			
			// Ecouter lorsque la cible est supprimée
			target.addEventListener(Event.REMOVED_FROM_STAGE, deleteMask);
			
			// Vérifier s'il a déjà un parent
			if (target.parent != null)
				drawMask();
		}
		
		// La méthode pour dessiner le mask
		protected function drawMask (e:Event = null):void
		{
			//Debug.add("com.zouloux.ui.Scroll.drawMask()");
			
			// Si le masque n'existe pas
			if (_mask == null)
			{
				// Mémoriser la position et décaller par rapport à la marge du haut
				_destination = _target.y - (_target is TextField ? 2 : 0);
				
				// Créer le masque en sprite
				_mask = new Sprite();
				
				// Ainsi que le fond
				_back = new Sprite();
				
				// Cibler le container
				var container:DisplayObjectContainer = (_target.parent as DisplayObjectContainer);
				
				// Cibler le parent de la cible et y ajouter le sprite oO'
				container.addChild(_mask);
				
				// Pareil pour le fond, mais en le mettant un étage en dessous de la cible
				container.addChildAt(_back, container.getChildIndex(_target) - 1);
				
				// Créer le cadre du masque
				_mask.graphics.beginFill(0x00FF00);
				_mask.graphics.drawRect(0, 0, _width, _height);
				_mask.graphics.endFill();
				
				// Et le même pour le fond
				_back.graphics.beginFill(0xFF0000, 0);
				_back.graphics.drawRect(0, 0, _width, _height);
				_back.graphics.endFill();
				
				// Placer le mask et le fond
				_back.x = _mask.x = _target.x;
				_back.y = _mask.y = _target.y;
				
				// Définir le masque de la cible
				_target.mask = _mask;
				
				// Ecouter les scroll
				_back.addEventListener(MouseEvent.MOUSE_WHEEL, scrolling);
				_target.addEventListener(MouseEvent.MOUSE_WHEEL, scrolling);
				
				// Démarrer le timer
				startTimer();
				
				// Décaller le textField ici
				if (_target is TextField)
					_target.y -= 2
			}
		}
		
		// La méthode pour supprimer le mask
		protected function deleteMask (e:Event = null):void
		{
			//Debug.add("com.zouloux.ui.Scroll.deleteMask()");
			
			// Ne plus écouter les scroll
			_back.removeEventListener(MouseEvent.MOUSE_WHEEL, scrolling);
			_target.removeEventListener(MouseEvent.MOUSE_WHEEL, scrolling);
			
			// Ne plus définir mask comme étant le masque
			_target.mask = null;
			
			// Et le virer du parent
			_mask.parent.removeChild(_mask);
			
			// Pareil pour le back
			_back.parent.removeChild(_back);
			
			// Supprimer les 2 références
			_mask = null;
			_back = null;
			
			// Arrêter le timer
			_timer.stop();
		}
		
		// Le scroll
		protected function scrolling (e:MouseEvent):void
		{
			//Debug.add("com.zouloux.ui.Scroll.deleteMask()");
			
			// Si la source a besoin d'être scrollée
			if (_target.height > _mask.height)
			{
				// Si c'est un textField
				if (_target is TextField)
				{
					// Récupérer le numéro de la ligne, limiter à 0
					var line:int = Math.max(0, (_target as TextField).getLineIndexAtPoint(_target.x + 2, _mask.y - _target.y + 2));
					
					// Mesurer la hauteur de ligne
					var lineMetrics:TextLineMetrics = (_target as TextField).getLineMetrics(line)
					var lineHeight:Number = lineMetrics.height + lineMetrics.leading;
					
					// Changer la position par rapport à la hauteur de la ligne
					_destination += (e.delta > 0 ? lineHeight : -lineHeight);
				}
				else
				{
					// Changer la position
					_destination += e.delta * 3;
				}
				
				// Démarrer le timer si c'est pas déjà fait
				startTimer();
			}
		}
		
		// La méthode pour redémarrer le timer
		protected function startTimer (e:Event = null)
		{
			// Démarrer le timer si c'est pas déjà fait
			if (!_timer.running)
				_timer.start();
		}
		
		// La boucle
		public function render (e:TimerEvent = null):void
		{
			// Mesurer la hauteur efféctive
			var totalHeight:Number = _mask.y - _target.height + _mask.height + 2;
			
			// Mesurer la topLimit
			var topLimit = _mask.y - 2;
			
			// Limiter la bordure du haut
			if (_destination > topLimit)
				_destination -= (_destination - topLimit) / 4;
			
			// Limiter la bordure du bas
			else if (_destination < totalHeight)
				_destination -= (_destination - totalHeight) / 4;
			
			// Actualiser la position
			_target.y -= (_target.y - _destination) / 5;
			
			// Si on est arrivés à destination, on arrête le timer
			// DESACTIVEE!
			if (_destination <= topLimit && _destination >= totalHeight && Math.abs(_destination - _target.y) < 0.2 && false)
			{
				_target.y = _destination;
				_timer.stop();
			}
			
			// Si le scrollView est associé, l'actualiser
			if (_scrollView != null)
			{
				_scrollView.contentHeight = _target.height;
				_scrollView.totalHeight = _height;
				_scrollView.position = (_target.y - topLimit) / (totalHeight - topLimit);
			}
			
			// Rendre l'animation fluide
			//e.updateAfterEvent();
		}
	}
}